package org.sxp.common.interceptor;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;
import com.baomidou.mybatisplus.extension.handlers.AbstractSqlParserHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.sxp.common.aspect.DataFilterAspect;
import org.sxp.common.dto.DataFilterDto;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.util.Map;
import java.util.Properties;

/**
 * 
 * @Author Administrator
 * @Date 2021/1/20
 */
@Slf4j
@Intercepts({
        @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class DataFilterInterceptor extends AbstractSqlParserHandler implements Interceptor {

    private static final String MATCHING_PREX = ".xml";
    private static final String MAPPED_STATEMENT_KEY = "delegate.mappedStatement";
    private static final String BOUND_SQL_KEY = "delegate.boundSql";
    private static final String LIMIT_KEY = " limit ";
    private static final String WHERE_KEY = " where ";
    private static final String EMPTY = "()";

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) PluginUtils.realTarget(invocation.getTarget());
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);

        MappedStatement mappedStatement = (MappedStatement)metaObject.getValue(MAPPED_STATEMENT_KEY);
        if(mappedStatement.getSqlCommandType() == SqlCommandType.INSERT){
            // insert插入不拼接过滤
            return invocation.proceed();
        }

        if(!mappedStatement.getResource().toLowerCase().contains(MATCHING_PREX)){
            // 一系列操作，目的是通过Dao找到实体类，再判断是否存在数据过滤的属性，如果没有就不生效，防止没有字段sql报错
            Class<?> classType = Class.forName(mappedStatement.getId().substring(0,mappedStatement.getId().lastIndexOf(".")));
            // 得到Dao接口类
            Type[] type=classType.getGenericInterfaces();
            // 因为是数组取出第一个
            ParameterizedType p=(ParameterizedType)type[0];
            // 得到泛型里面的实体类
            Class entityClass=(Class) p.getActualTypeArguments()[0];

            TableName tableName = (TableName)entityClass.getAnnotation(TableName.class);

            DataFilterDto dataFilterDto = DataFilterAspect.getCurrentDataFilter();
            log.debug("数据权限过滤用户条件：{}", JSON.toJSONString(dataFilterDto));
            if(dataFilterDto != null && !dataFilterDto.getColumnSql().isEmpty()){
                StringBuilder sqlFilterBuf = new StringBuilder("(");
                Map<String, ColumnCache> columnMap = LambdaUtils.getColumnMap(entityClass);
                Map<String, String> sqlFilter =  dataFilterDto.getColumnSql();
                sqlFilter.forEach((columnKey, sqlStr) -> {
                    columnMap.forEach((s, columnCache) -> {
                        try{
                            if(columnCache.getColumn().toLowerCase().equals(columnKey.toLowerCase())){
                                if (sqlFilterBuf.length() > 1) {
                                    sqlFilterBuf.append(" and ");
                                }
                                sqlFilterBuf.append(sqlStr);
                                return;
                            }
                        }catch (Exception ex){
                            log.error(ex.getMessage(), ex);
                            return;
                        }
                    });
                });
                sqlFilterBuf.append(")");
                log.debug("数据权限过滤拼接结果：{}", sqlFilterBuf.toString());
                if(!sqlFilterBuf.toString().equals(EMPTY)) {
                    // sql过滤条件不为空，修改原始sql
                    BoundSql boundSql = (BoundSql) metaObject.getValue(BOUND_SQL_KEY);
                    String sqlStr = boundSql.getSql();
                    StringBuilder originalSql = new StringBuilder(sqlStr);

                    int whereIndex = sqlStr.toLowerCase().indexOf(WHERE_KEY);
                    if (whereIndex > 0) {
                        // 有where关键字，插入在where后面
                        sqlFilterBuf.insert(sqlFilterBuf.length(), " and ");
                        originalSql.insert(whereIndex + WHERE_KEY.length(), sqlFilterBuf);
                    } else {
                        // 无where关键字，插入在表后面
                        sqlFilterBuf.insert(0, " where ");
                        if(tableName != null){
                            int index = sqlStr.toLowerCase().indexOf(tableName.value());
                            if(index > 0){
                                originalSql.insert(index + tableName.value().length(), sqlFilterBuf);
                            }else{
                                log.error("未定位到表名，数据权限忽略");
                            }
                        }

                    }
                    // 通过反射修改sql语句
                    Field field = boundSql.getClass().getDeclaredField("sql");
                    field.setAccessible(true);
                    field.set(boundSql, originalSql.toString());
                }
            }
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return target instanceof StatementHandler ? Plugin.wrap(target, this) : target;
    }

    @Override
    public void setProperties(Properties properties) {

    }
}
